package main

import (
	"context"
	"errors"
	"fmt"
	"net"
	"net/url"
	"time"

	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
)

type handler struct {
	conn      net.Conn
	close     chan struct{}
	recv      chan []byte
	heartbeat time.Duration
}

func connect(asddr string) (*handler, error) {

	_, err := url.Parse(asddr)
	if err != nil {
		return nil, err
	}

	conn, _, _, err := ws.Dial(context.Background(), asddr)
	if err != nil {
		return nil, err
	}

	h := handler{
		conn:      conn,
		close:     make(chan struct{}, 1),
		recv:      make(chan []byte, 10),
		heartbeat: 1 * time.Second,
	}

	go h.heartbeatloop()

	go func() {
		err := h.readloop(conn)
		if err != nil {
			fmt.Println(err)
		}
		h.close <- struct{}{}
	}()

	return &h, nil
}

func (h *handler) readloop(conn net.Conn) error {
	fmt.Println("readloop started")
	err := h.conn.SetReadDeadline(time.Now().Add(h.heartbeat * 3))
	if err != nil {
		return err
	}
	for {
		frame, err := ws.ReadFrame(conn)
		if err != nil {
			return err
		}

		if frame.Header.OpCode == ws.OpClose {
			return errors.New("remote sidse close the channel")
		}
		if frame.Header.OpCode == ws.OpText {
			h.recv <- frame.Payload
		}
		if frame.Header.OpCode == ws.OpPong {
			_ = h.conn.SetReadDeadline(time.Now().Add(h.heartbeat * 3))
		}
	}
}

func (h *handler) sendText(message string) error {

	fmt.Printf("send message:%s", message)
	return wsutil.WriteClientText(h.conn, []byte(message))
}

func run(ctx context.Context) error {
	url := "ws://192.168.0.242:8005?user=jsb"
	h, err := connect(url)
	if err != nil {
		return err
	}

	go func() {
		for msg := range h.recv {
			fmt.Printf("recv msg:%s\n", msg)
		}
	}()

	tk := time.NewTicker(time.Second * 1)
	for {
		select {
		case <-tk.C:
			err := h.sendText("hello room")
			if err != nil {
				fmt.Println(err)
			}
		case <-h.close:
			fmt.Println("connection closed")
			return nil
		}
	}
}

func (h *handler) heartbeatloop() error {

	fmt.Println("headrbeatloop started")
	tick := time.NewTicker(h.heartbeat * 1)

	for range tick.C {
		fmt.Println("ping")
		if err := wsutil.WriteClientMessage(h.conn, ws.OpPing, nil); err != nil {
			return err
		}
	}
	return nil
}

func main() {

	ctx := context.Background()

	err := run(ctx)
	if err != nil {
		panic(err)
	}
}
